package Simulation;

import java.util.Random;

import Counter.Counter;
import Counter.DCounter;
import Counter.DiscreteHistogram;
import RandVars.ErlangRandVar;
import RandVars.ExpRandVar;
import RandVars.GIRandVar;
import RandVars.RandVar;
import RandVars.Rng;

public class DES {

	public static final int TICKS_PER_SECOND = 100;

	// Switch to chose different simulation times
	public static final int SIM_TIME_SHORT = 1000;
	public static final int SIM_TIME_LONG = 10000;
	public static final int SIM_TIME_EXERCISE2 = (int) Math.pow(10, 5)
			* TICKS_PER_SECOND;

	public static final int SIM_TIME = SIM_TIME_EXERCISE2;

	// Set time between customer arrivals(currently unused)
	public static final double REGULAR_INTER_ARIVAL_TIME = TICKS_PER_SECOND / (9.0);

	// Set average service completion time
	public static final double SERVICE_COMPLETION_TIME = TICKS_PER_SECOND / 1;

	// Set the amount of servers
	public static final int TOTAL_SERVERS = 10;

	
	// Set maximum queue length (-1 for infinite)
	public static final double MAX_QUEUE_LENGTH = -1;

	// Switch to set a mode for less preferable waiting slots
	public static final boolean A3_E1 = false;
	// Set the number of dry waiting slots
	public static final int A3_E1_DRY_WAITING_SLOTS = 10;
	// Set the arrival rate for less preferable waiting slots
	public static final double A3_E1_INTER_ARIVAL_TIME_DRY = TICKS_PER_SECOND / 10;
	public static final double A3_E1_INTER_ARIVAL_TIME_WET = TICKS_PER_SECOND / 5;

	// Switch to set a mode for customer deadlines
	public static final boolean A3_E2 = false;

	// Switch to set lazy cashier threshold - Exercise 6.1
	public static final boolean lazyThresholdOn = false;
	public static final int lazyThresholdQueueLength = 10;
	public static final int lazyThresholdPercentage = 50;

	public static double inter_arrival_time = REGULAR_INTER_ARIVAL_TIME;

	// Aktuelles Event
	protected static SimEvent e;

	// Simulationsstatus als Singleton
	private static SimState state = SimState.instance;

	public static void main(String[] args) {
		// randomNumberTest();

		runSimulation();
		// runHistogramTest();
	}

	@SuppressWarnings("unused")
	private static void runHistogramTest() {
		Random random = new Random();
		// Rng rng=new Rng();
		// RandVar rv = ;
		// GeoRandVar grv=new GeoRandVar(0.1);
		DiscreteHistogram dh = new DiscreteHistogram(20, 0, 100, "test");

		for (int i = 0; i < 10000000; i++) {
			double x = random.nextGaussian() * 2 + 10;
			// double x=rv.getDouble();
			dh.count(x);
		}
		dh.report();
	}

	private static void runSimulation() {
		// Initialisierung
		e = null;
		// We start with a single CustomerArrival Event at 0 seconds
		state.ec.add(new CustomerArrival(0));
		// We also add a SimulationTermination Event at the desired end time
		state.ec.add(new SimulationTermination(SIM_TIME));
		// Simulation loop
		while (state.stop != true) {
			// Set now to the time of the earliest unhandled event
			state.now = ((SimEvent) state.ec.peek()).getSimTime();
			// Iterate through all events happening at this timestep
			do {
				e = getNextEvent();
				// System.out.println("Time: " + e.getSimTime() + " Event: "
				// + e.toString() + " Queuesize: " + state.queueSize);
				e.process();
			} while ((!state.ec.isEmpty())
					&& ((SimEvent) state.ec.peek()).getSimTime() == state.now);
		}
		printStatistics();
	}

	private static void printStatistics() {

		System.out.println("Finished - min queue: " + state.min
				+ " max queue: " + state.max);
		System.out.println("Statistics:\nwt=" + state.waiting_time.getMean());
		System.out.println("wt_max=" + state.waiting_time.getMax());
		System.out.println("pt=" + state.processing_time.getMean());
		System.out.println("pt_max=" + state.processing_time.getMax());
		System.out.println("rt=" + state.retention_time.getMean());
		System.out.println("rt_max=" + state.retention_time.getMax());
		System.out.println("wql=" + state.waiting_queue_length.getMean());
		System.out.println("wql_max=" + state.waiting_queue_length.getMax());
		System.out.println("Server utilization percentage="
				+ state.utilization.getUtilization(state.now));
		System.out.println("cbp=" + state.customer_blocked.getMean());
		System.out.println("dle=" + state.deadline_expiration.getMean());

		System.out.println(state.customer_blocked.amount);

		System.out.println("Left in queue at the time of termination "
				+ SimState.instance.waiting_queue.size());
		System.out.println("Customer total: " + CustomerArrival.counter);
		System.out.println("Customer served: " + ServiceCompletion.counter);
		System.out.println("Now: " + state.now);

//		state.ch_wql.report();
//		state.dh_cwt.report();

	}

	// Gets the event which will happen next from the event queue
	private static SimEvent getNextEvent() {
		return (SimEvent) state.ec.poll();
	}

	@SuppressWarnings("unused")
	private static void randomNumberTest2() {
		RandVar var = new GIRandVar(10, 200);
		int i = 0;
		long sum = 0;
		while (i < 1000000) {
			long randomNumber = var.getLongRV();
			System.out.println("var: " + randomNumber);
			sum += randomNumber;
			i++;
		}
		System.out.println("mean: " + sum / 1000000);
	}

	@SuppressWarnings("unused")
	private static void randomNumberTest() {
		// Rng var = new Rng(65539,0,19,201);
		// DCounter counter = new DCounter(true);
		// double randomNumber;
		// for (int i = 0; i < 10000; i++) {
		// randomNumber = var.nextDouble();
		// counter.count(randomNumber);
		// }
		// for (int i = 0; i <= 10; i++) {
		// System.out.println("Lag: " + i + " Autocor= "
		// + counter.autoCorrelation(i));
		// }
		// System.out.println("mean: " + sum / 1000000);

		Rng configA = new Rng();
		Rng configB = new Rng(65539, 0, 1, 201);
		Rng configC = new Rng(65539, 0, 19, 201);
		//
		RandVar iat = new ErlangRandVar(10, 10, new Rng());
		RandVar sct = new ExpRandVar(DES.SERVICE_COMPLETION_TIME, configB);
		Counter test = new DCounter();
		for (int i = 0; i < 10000; i++) {
			test.count(iat.getLongRV());
		}
		System.out.println(test.amount);
	}
}
